package rsa

import (
	"codeup.aliyun.com/lsxd/zltx/adapter/utils/formater"
	"github.com/dromara/dongle"
)

type KeyType int

const (
	Pkcs1 KeyType = iota // PKCS#1
	Pkcs8                // PKCS#8
)

type Options struct {
	format     formater.Formater
	reversion  bool // 是否需要反向操作,也就是加密时使用私钥解密,解密时使用公钥加密
	keyType    KeyType
	privateKey string
	publicKey  string
}

// SetFormat 设置输出格式
func (o *Options) SetFormat(f formater.Formater) {
	o.format = f
}

// SetKeyType 设置密钥类型
func (o *Options) SetKeyType(t KeyType) {
	o.keyType = t
}

// SetPrivateKey 设置私钥
func (o *Options) SetPrivateKey(key string) {
	o.privateKey = key
}

// SetPublicKey 设置公钥
func (o *Options) SetPublicKey(key string) {
	o.publicKey = key
}

// SetReversion 设置是否需要反向操作
func (o *Options) SetReversion(r bool) {
	o.reversion = r
}

// merge
func (o *Options) merge(n *Options) {
	if n == nil {
		return
	}
	o.reversion = n.reversion
	if n.format != nil {
		o.format = n.format
	}
	if n.keyType != 0 {
		o.keyType = n.keyType
	}
	if n.privateKey != "" {
		o.privateKey = n.privateKey
	}
	if n.publicKey != "" {
		o.publicKey = n.publicKey
	}
}

type Rsa struct {
	o *Options
}

// NewRsa 创建一个新的Rsa对象
func NewRsa(o *Options) *Rsa {
	opt := &Options{
		format:    formater.Base64,
		reversion: false,
		keyType:   Pkcs1,
	}
	opt.merge(o)
	return &Rsa{o: opt}
}

// Encrypt 加密数据
func (d *Rsa) Encrypt(data string) (string, error) {
	// 判断是否需要反向操作
	var key string
	if d.o.reversion {
		key = formatPrivateKey(d.o.keyType, d.o.privateKey)
	} else {
		key = formatPublicKey(d.o.keyType, d.o.publicKey)
	}
	des := dongle.Encrypt.FromString(data).ByRsa([]byte(key))
	if err := des.Error; err != nil {
		return "", err
	}
	raw := des.ToRawBytes()
	return d.o.format.Encode(raw), nil
}

// Decrypt 解密数据
func (d *Rsa) Decrypt(data string) (string, error) {
	content, err := d.o.format.Decode(data)
	if err != nil {
		return "", err
	}
	// 判断是否需要反向操作
	var key string
	if d.o.reversion {
		key = formatPublicKey(d.o.keyType, d.o.publicKey)
	} else {
		key = formatPrivateKey(d.o.keyType, d.o.privateKey)
	}
	des := dongle.Decrypt.FromRawBytes(content).ByRsa([]byte(key))
	if err := des.Error; err != nil {
		return "", err
	}
	return des.ToString(), nil
}
